package main

import (
	"bufio"
	"context"
	"os/signal"
	"sync"
	"syscall"

	//"encoding/json"
	"fmt"
	//"io/ioutil"
	"log"
	//"os"
	"math/rand"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/chromedp/chromedp" // 爬虫库
	"github.com/xuri/excelize/v2"  // excel库
)
const loginURL = "http://133.0.218.243:31668/easkWeb/index.html"
var quest  []string //用于存放从excel读取的数据
var token  string   //保存token
var rows   int      //记录行数，自动删除excel
var my_mux sync.Mutex // 互斥操作rows
var Pause   chan bool  //监听暂停
var IsPause bool = false //设置暂停，默认不暂停
func main() {
	var gn int //用于选择功能
	//设置中断响应
	sig := make(chan os.Signal,1) //设置一个大小为1的通道
	signal.Notify(sig,os.Interrupt,syscall.SIGTERM) //使用Notify监听
	//协程处理中断
	go func() {	
		<-sig
		my_mux.Lock()
		trueRow := rows
		my_mux.Unlock()
		log.Println("检测到Ctrl^C,正在准备退出程序...")
		log.Printf("当前已经成功发送了%d行数据,正在重新保存excel...",trueRow)
		delete_rows()
		log.Println("数据保存完毕,程序退出!")
		os.Exit(0)
	}()

	// 设置随机种子
	rand.Seed(time.Now().UnixNano())
	//从excel中获取数据
	println("初始化设置中，正在获取数据")
	quest = get_data() //将获取的数据存放在quest中
	println("数据获取完毕")
	//初始化chromedp
	ctx := chrom_init()
	// 初始化浏览器实例
	_ = chromedp.Run(ctx)  
	// 确保在程序结束时清理资源ctx
	defer func() {
		if r := recover(); r != nil {
			log.Printf("程序发生panic: %v", r)
		}
	}()
	// 执行登陆任务
	if err := chromedp.Run(ctx, login_Tasks()); err != nil {
		log.Printf("登录失败: %v", err)
		return
	}
	log.Printf("获取到token:%s\n", token)
	log.Printf("tip:不要随便试,如果当前没有待结案的工单,选择2那么就会无效操作,这部分使用chromedp完全模拟手工点击的,没有当前工单的状态以及id等数据,如果页面没有当前的选择,error!,确认好当前状态再选择!\n请选择功能:\n1.进行工单自助发和结案\n2.进行工单自助结案(适用于当前还有一些工单没有结案,自动结案,确保已经正确进入页面,并且都是等待处理的单子,否则运行将出现无法期遇的错误！)\n请选择(输入数字):")
	fmt.Scanf("%d",&gn)
	switch gn {
	case 1:
		// 使用单个 Run 执行所有任务，避免多次创建上下文
		if err := chromedp.Run(ctx, processAllTasks()); err != nil {
			log.Printf("任务执行失败: %v", err)
			return
		}
		log.Println("所有任务执行完成")
	case 2:
		if err := chromedp.Run(ctx, endWorkfile()); err != nil {
			log.Printf("任务执行失败: %v", err)
			return
		}
		log.Println("待结案工单处理完成")
	default:
		println("错误选择,退出desewa!")
		return 
	}
}
// 读取excel获取数据
func get_data() []string {
	var data []string
	//打开并读取excel
	f, err := excelize.OpenFile("2025年6月记录工单表格.xlsx")
	if err != nil {
		println(err)
	}
	//如果当前列不是空则取出来，并存放在quest中,上午发230，下午发230
	for i := 1; i <= 230; i++ {
		cell := fmt.Sprintf("C%d", i)
		cellvalue, err := f.GetCellValue("Sheet1", cell)
		if(cellvalue == "") {
			break; //如果遇到空数据则退出
		}
		if err != nil {
			println(err)
		}
		data = append(data, cellvalue)
	}
	f.Close() //关闭
	return data
}
//删除已经成功发送数据的行
func delete_rows() {
	f, err := excelize.OpenFile("2025年6月记录工单表格.xlsx")
	defer func() {
		_ = f.Close()
	}()
	//删除行
	for i := 1; i <= rows; i++ {
		if err = f.RemoveRow("Sheet1", 1); err != nil {
			fmt.Println(err)
			return
		}
	}
	//保存文件
	_ = f.Save()
	println("已删除发送过的数据")
}
//使用打开的游览器
func chrom_init() context.Context {
	// 首先尝试连接到已打开的 Chrome 浏览器
	ctx, err := connectToExistingChrome()
	if err == nil {
		fmt.Println("成功连接到已打开的 Chrome 浏览器")
		return ctx
	}
	fmt.Println("未找到已打开的 Chrome 浏览器，启动新实例...")
	return new_chrome()
}

// 连接到已存在的 Chrome 实例（使用远程调试）
func connectToExistingChrome() (context.Context, error) {
	//Chrome端口
	ports := []string{"9222", "9223", "9224", "9225", "9226"}
	for _, port := range ports {
		debugURL := fmt.Sprintf("http://localhost:%s", port)
		fmt.Printf("尝试连接 Chrome 调试端口 %s...\n", port)
		// 创建远程分配器
		allocatorContext, cancel := chromedp.NewRemoteAllocator(context.Background(), debugURL)
		// 创建上下文
		ctx, cancel := chromedp.NewContext(allocatorContext, chromedp.WithLogf(log.Printf))
		// 测试连接
		if err := chromedp.Run(ctx, chromedp.Navigate("about:blank")); err != nil {
			cancel()
			continue
		}
		fmt.Printf("成功连接到 Chrome 调试端口 %s\n", port)
		// 返回成功连接的上下文
		return ctx, nil
	}
	return nil, fmt.Errorf("无法连接到任何 Chrome 调试端口")
}

//如果没有手动打开，则自动打开一个
func new_chrome() context.Context {
	userDataDir := "./browser-session"
	os.MkdirAll(userDataDir, 0755)
	// chromdp依赖context上传递参数
	ctx, cancel := chromedp.NewExecAllocator(
		context.Background(),
		
		// 以默认配置的数组为基础，覆写headless参数
		// 当然也可以根据自己的需要进行修改，这个flag是浏览器的设置
		append(
			chromedp.DefaultExecAllocatorOptions[:],
			chromedp.Flag("headless", false),   //有头
			chromedp.UserDataDir(userDataDir),  //加载用户配置
			chromedp.Flag("disable-gpu", true), // 禁用GPU加速，提高稳定性
			chromedp.Flag("no-sandbox", true),  // 在Windows上可能需要
			chromedp.Flag("disable-dev-shm-usage", true), // 禁用共享内存
			//chromedp.ExecPath("C:/Users/Administrator/AppData/Local/Google/Chrome/Bin/chrome.exe"), //设置googel路径
		)...,
	)
	// 创建新的chromedp上下文对象，设置更长的超时时间
	ctx, cancel = context.WithTimeout(ctx, 300*time.Second) // 延长超时时间
	ctx, cancel = chromedp.NewContext(
		ctx,
		// 设置日志方法
		chromedp.WithLogf(log.Printf),
	)
	// 在程序结束时正确清理资源
	go func() {
		<-ctx.Done()
		cancel()
	}()
	return ctx
}
// 步骤
// 1.检查是否有cooki  如果有直接进入，没有进入登陆
// 2.检查登陆状态
// 登陆任务
func login_Tasks() chromedp.Tasks {
	return chromedp.Tasks{
		//首先加载cooki
		//loadCookies(),
		// 打开易问的登陆界面
		chromedp.Navigate(loginURL),
		// 检查是否登陆,没有自动登录则手动登陆
		checkLoginStatus(),
		// 成功登陆获取token,执行 JavaScript 获取 token
		// 先等待页面加载完成
		chromedp.Sleep(3 * time.Second),
		// 然后尝试获取 token
		chromedp.Evaluate(`localStorage.getItem('token')`, &token),
		//成功登陆保存cooki
		//saveCookies(),
	}
}
//方法1模拟点击
// 1.成功登陆点击提问题 #app > div.frame-wrapper > div > div > div.tc > button
// 2.提问内容嵌入输入框body > div.el-dialog__wrapper > div > div.el-dialog__body > form > div.el-form-item.quest-raise.is-required.el-form-item--small > div > div > textarea
// 提交易问
/* func do_Tasks() chromedp.Tasks {
	return chromedp.Tasks{
		// 1. 点击提问题
		chromedp.Click(`#app > div.frame-wrapper > div > div > div.tc > button`,chromedp.NodeVisible),
		// 2.
		// 3. 点击确认按钮
		// #dialog > div.dialog-wrapper > div > div.dialog-footer > div.dialog-footer-ok
		//chromedp.Click(`#dialog > div.dialog-wrapper > div > div.dialog-footer > div.dialog-footer-ok`),
	}
}
*/
// 处理所有任务的单个函数
func processAllTasks() chromedp.Tasks {
	return chromedp.Tasks{
		chromedp.ActionFunc(func(ctx context.Context) error {
			successCount := 0
			PauseListen() //监听暂停操作
			for i := 0; i < len(quest); i++ {
				//检查暂停，chromedp.Run()是原子操作，发送过程中不能暂停，循环开始时候检查
				if IsPause {
					log.Println("当前处于暂停状态，等待继续...")
					for IsPause { // 等待信号
						time.Sleep(1 * time.Second)
					}
				}
				// 检查上下文是否有效
				select {
				case <-ctx.Done():
					log.Println("上下文已取消，停止执行")
					return nil
				default:
				}
				log.Printf("正在发送第 %d/%d 条消息: %s\n", i+1, len(quest), quest[i])
				// 直接执行提交，不创建新的 Tasks
				if err := submitQuestion(ctx, quest[i]); err != nil {
					log.Printf("第 %d 条消息发送失败: %v", i+1, err)
					continue
				}
				log.Printf("发送消息(%s)成功\n",quest[i])
				successCount++
				//随机30S到1分钟发一单
				//randomNum := rand.Intn(31)+30
				randomNum := rand.Intn(6)+20
				log.Printf("等待 %d 秒...\n", randomNum)
				time.Sleep(time.Duration(randomNum) * time.Second)
			}
			log.Printf("任务完成，成功发送 %d/%d 条消息\n", successCount, len(quest))
			my_mux.Lock()
			rows = successCount //成功发送数据的行数
			//处理表格，删除已经发送过的
			delete_rows()
			my_mux.Unlock()
			return nil
		}),
	}
}
//处理结案工单
func endWorkfile() chromedp.Tasks {
	return chromedp.Tasks{
		chromedp.ActionFunc(func(ctx context.Context) error {
			// 获取页面的数字（innerText 返回字符串）
			var endnumStr string
			if err := chromedp.Evaluate(`document.querySelector("#tab-unFinishPage > span > span.color-main").innerText`, &endnumStr).Do(ctx); err != nil {
				log.Printf("获取待结案工单数失败: %v", err)
				return err
			}
			//字符转int
			endnum, _ := strconv.Atoi(strings.TrimSpace(endnumStr))
			log.Printf("当前待结案工单数为:%d,正在处理...", endnum)
			// 处理工单
			for j := 0; j < endnum; j++ {
				log.Printf("处理第 %d/%d 个工单", j+1, endnum)
				if err := chromedp.Run(ctx,
					chromedp.Click(`#pane-unFinishPage > div > div.flex-space-between.align-center.pdl-15.pdr-15 > div:nth-child(2) > i.news-func.icon-refresh.mgl-10`, chromedp.NodeVisible),
					chromedp.Sleep(1*time.Second), // 短暂等待,
					//2. 点击一个待结案工单
					chromedp.Click(`#pane-unFinishPage > div > div.el-checkbox-group.flex-1.hover-scorll.mgt-10 > div.news-mail-item.on`, chromedp.NodeVisible),
					chromedp.Sleep(1*time.Second),
					//3. 点击结案
					chromedp.Click(`#app > div.frame-wrapper > div > div > div.qus-right-side.hig-row > div > div.qus-cont-detail > div.qus-detail-top > div.clearfix > div > div:nth-child(1) > button:nth-child(1)`, chromedp.NodeVisible),
					chromedp.Sleep(2*time.Second),
					chromedp.WaitVisible(`body > div.el-dialog__wrapper > div > div.el-dialog__body`, chromedp.ByQuery), //等待弹窗
					//4. 点击确定
					chromedp.Click(`body > div.el-dialog__wrapper > div > div.el-dialog__footer > div > button.el-button.btn-wid2.btn-bgc-main.fs-16.el-button--primary`, chromedp.NodeVisible),
					chromedp.Sleep(1*time.Second), // 短暂等待
				); err != nil { //处理工单
					log.Printf("处理第 %d 个工单时出错: %v", j+1, err)
					// 可以选择继续处理下一个工单，或者中断
					continue
				}
			}
			return nil
		}),
	}
}

//结束工单
/* func endWorkfile() chromedp.Tasks {
	return chromedp.Tasks{
		chromedp.Click(`#pane-unFinishPage > div > div.flex-space-between.align-center.pdl-15.pdr-15 > div:nth-child(2) > i.news-func.icon-refresh.mgl-10`,chromedp.NodeVisible),
		chromedp.Sleep(1*time.Second), // 短暂等待,
		//2. 点击一个待结案工单
		chromedp.Click(`#pane-unFinishPage > div > div.el-checkbox-group.flex-1.hover-scorll.mgt-10 > div.news-mail-item.on`,chromedp.NodeVisible),
		chromedp.Sleep(1*time.Second),
		//3. 点击结案
		chromedp.Click(`#app > div.frame-wrapper > div > div > div.qus-right-side.hig-row > div > div.qus-cont-detail > div.qus-detail-top > div.clearfix > div > div:nth-child(1) > button:nth-child(1)`,chromedp.NodeVisible),
		chromedp.Sleep(2*time.Second),
		chromedp.WaitVisible(`body > div.el-dialog__wrapper > div > div.el-dialog__body`, chromedp.ByQuery), //等待弹窗
		//4. 点击确定
		chromedp.Click(`body > div.el-dialog__wrapper > div > div.el-dialog__footer > div > button.el-button.btn-wid2.btn-bgc-main.fs-16.el-button--primary`,chromedp.NodeVisible),
		chromedp.Sleep(1*time.Second), // 短暂等待
	}
} */

// 直接提交问题的函数，不返回 Tasks
func submitQuestion(ctx context.Context, question string) error {
	var result map[string]interface{} // 接受返回之
	return chromedp.Evaluate(fmt.Sprintf(`
		(async function() {
			const formData = {
				"acceptanceNumber": "",
				"camera": [],
				"files": [],
				"is_urgent": "N",
				"lable_id":  "",
				"parentId": "1261_43839",
				"parentType": "3",
				"question_content": "%s",
				"question_title": "",
				"service_type_id": "",
				"staff_id": "1602",
				"target_province": "HB",
				"target_staff_id": "352096",
				"token": "%s",
				"type": "1"
			};
			try {
				const response = await fetch('http://133.0.218.243:31668/throwbill/CXF/rest/questionservice/newquestion', {
					method: 'POST',
					headers: {
						'Content-Type': 'application/json',
					},
					body: JSON.stringify(formData)
				});
				if (!response.ok) {
					throw new Error('HTTP错误! 状态: ' + response.status);
				}
				const result = await response.json();
				console.log('提交成功:', result);
				return {success: true, data: result};
			} catch (error) {
				console.error('提交失败:', error);
				return {success: false, error: error.message};
			}
		})()
	`, question, token), &result).Do(ctx)
}

// 获取工单列表 POST 请求url:http://133.0.218.243:31668/throwbill/CXF/rest/questionservice/qryquestionlistwithtag
//请求提: {"sp_tag":"0","order_type":"1","page_no":"1","page_size":"20","staff_id":"1602","token":"pc_1761026773670_557","keywords":"","beginDate":"","endDate":""}
//需要返回体数据： 工单数："count", 是否最后一页(一页20条)："is_last_page" 当前20个工单的id : "question_id"，但返回提数据结构复杂

// 处理结案工单 POST 请求url：http://133.0.218.243:31668/throwbill/CXF/rest/questionservice/handlequestion
//请求体：{"token":"pc_1761026773670_557","staff_id":"1602","estimator":"1602","expert":"1602","question_dispather_id":"1602","question_operation":"7","score":"100","to_expert":"","type":"1","banjieFlag":"","question_id":"527983","question_owner_id":"1602","estimate_level":"5","solve_type":"1","cause_option":"14,15,20","cause_content":""}
// 如果没有登陆等待30s手动登陆，如果30s内还是没有登陆则直接退出
//相比与上一个这个更适合用手点击模拟操作
func checkLoginStatus() chromedp.ActionFunc {
	return func(ctx context.Context) (err error) {
		var url string
		if err = chromedp.Evaluate(`window.location.href`, &url).Do(ctx); err != nil {
			return
		}
		// 检查是否在首页（已登录）
		if strings.Contains(url, "http://133.0.218.243:31668/easkWeb/index.html#/mainPage/homeIndex") {
			log.Println("已经使用cookies登陆")
			return nil
		}
		// 如果没有登录，等待30秒手动登录
		log.Println("未检测到登录状态，请在60秒内手动登录...")
		// 创建60秒超时的上下文
		loginCtx, cancel := context.WithTimeout(ctx, 60*time.Second)
		defer cancel()
		// 轮询检查登录状态
		ticker := time.NewTicker(2 * time.Second) // 每2秒检查一次
		defer ticker.Stop()
		for {
			select { //使用select处理异步IO
			case <-loginCtx.Done():
				return fmt.Errorf("登录超时，请在60秒内完成登录")
			case <-ticker.C:
				// 再次检查URL
				if err = chromedp.Evaluate(`window.location.href`, &url).Do(ctx); err != nil {
					return err
				}
				if strings.Contains(url, "http://133.0.218.243:31668/easkWeb/index.html#/mainPage/homeIndex") {
					log.Println("手动登录成功！")
					return nil
				}
				//log.Println("等待登录中...当前页面:", url)
			}
		}
	}
}

//监听暂停

func PauseListen() {
	go func () {
		var temp int
		//当按下p触发信号
		reader := bufio.NewReader(os.Stdin) //从缓冲区读
		for {
			input,_ := reader.ReadString('p')
			if strings.TrimSpace(input) == "p" {
				if !IsPause {
					log.Println("暂停中...")
					Pause  <- true
					IsPause = true
					println("输入1继续发工单，输入2执行结案工单处理")
					fmt.Scanf("%d",&temp)
					switch temp {
						case 1:
							log.Println("继续发工单中...")
							Pause <- false
							IsPause = false
						case 2:
							log.Println("执行结案工单处理...")
							ctxnew := chrom_init() // 创建新的上下文
							if err := chromedp.Run(ctxnew, endWorkfile()); err != nil {
								log.Printf("任务执行失败: %v", err)
								return
							}
							log.Println("待结案工单处理完成")
							println("1. 继续发送工单")
							println("2. 退出程序")
							print("请选择(输入数字): ")
							
							var choice int
							fmt.Scanf("%d", &choice)
							
							if choice == 1 {
								log.Println("继续发送工单...")
								IsPause = false
							} else {
								log.Printf("当前已经成功发送了%d行数据,正在重新保存excel...", rows)
								delete_rows()
								log.Println("数据保存完毕,程序退出!")
								os.Exit(0)
							}
						default:
							log.Println("无效输入，请输入1或2")
					}
				}
			}
		}
	}()
}